# Implementation of XEP-0118 "User Tune"

namespace eval tune {
    variable node http://jabber.org/protocol/tune
    variable substatus
    variable tune

    custom::defvar options(auto-subscribe) 0 \
	[::msgcat::mc "Auto-subscribe to other's user tune notifications."] \
	-command [namespace current]::register_in_disco \
	-group PEP -type boolean

    pubsub::register_event_notification_handler $node \
	    [namespace current]::process_tune_notification
    hook::add user_tune_notification_hook \
	    [namespace current]::notify_via_status_message

    hook::add finload_hook \
	    [namespace current]::on_init 60
    hook::add roster_jid_popup_menu_hook \
	    [namespace current]::add_roster_pep_menu_item
    hook::add roster_user_popup_info_hook \
	    [namespace current]::provide_roster_popup_info
    hook::add userinfo_hook \
	    [namespace current]::provide_userinfo

    disco::register_feature $node
}

proc tune::register_in_disco {args} {
    variable options
    variable node

    if {$options(auto-subscribe)} {
       disco::register_feature $node+notify
    } else {
       disco::unregister_feature $node+notify
    }
}

proc tune::add_roster_pep_menu_item {m connid jid} {
    set rjid [roster::find_jid $connid $jid]

    if {$rjid == ""} {
 	set rjid [node_and_server_from_jid $jid]
    }

    set pm [pep::get_roster_menu_pep_submenu $m $connid $rjid]

    set mm [menu $pm.tune -tearoff no]
    $pm add cascade -menu $mm \
	    -label [::msgcat::mc "User tune"]

    $mm add command \
	    -label [::msgcat::mc "Subscribe"] \
	    -command [list [namespace current]::subscribe $connid $rjid]
    $mm add command \
	    -label [::msgcat::mc "Unsubscribe"] \
	    -command [list [namespace current]::unsubscribe $connid $rjid]

    hook::run roster_pep_user_tune_menu_hook $mm $connid $rjid
}

proc tune::subscribe {connid jid args} {
    variable node
    variable substatus

    set to [node_and_server_from_jid $jid]
    set cmd [linsert $args 0 [namespace current]::subscribe_result $connid $to]
    pep::subscribe $to $node \
	    -connection $connid \
	    -command $cmd
    set substatus($connid,$to) sent-subscribe
}

proc tune::unsubscribe {connid jid args} {
    variable node
    variable substatus

    set to [node_and_server_from_jid $jid]
    set cmd [linsert $args 0 [namespace current]::unsubscribe_result $connid $to]
    pep::unsubscribe $to $node \
	    -connection $connid \
	    -command $cmd
    set substatus($connid,$to) sent-unsubscribe
}

# Err may be one of: OK, ERR and DISCONNECT
proc tune::subscribe_result {connid jid res child args} {
    variable substatus

    set cmd ""
    foreach {opt val} $args {
	switch -- $opt {
	    -command {
		set cmd $val
	    }
	    default {
		return -code error "unknown option: $opt"
	    }
	}
    }

    switch -- $res {
	OK {
	    set substatus($connid,$jid) from
	}
	ERR {
	    set substatus($connid,$jid) error
	}
	default {
	    return
	}
    }

    if {$cmd != ""} {
	lappend cmd $jid $res $child
	eval $cmd
    }
}

proc tune::unsubscribe_result {connid jid res child args} {
    variable substatus
    variable tune

    set cmd ""
    foreach {opt val} $args {
	switch -- $opt {
	    -command {
		set cmd $val
	    }
	    default {
		return -code error "unknown option: $opt"
	    }
	}
    }
    
    if {[string equal $res OK]} {
	set substatus($connid,$jid) none
	catch {unset tune(artist,$connid,$jid)}
	catch {unset tune(length,$connid,$jid)}
	catch {unset tune(source,$connid,$jid)}
	catch {unset tune(title,$connid,$jid)}
	catch {unset tune(track,$connid,$jid)}
	catch {unset tune(uri,$connid,$jid)}
    }

    if {$cmd != ""} {
	lappend cmd $jid $res $child
	eval $cmd
    }
}

proc tune::provide_roster_popup_info {var connid user} {
    variable substatus
    variable tune

    upvar 0 $var info

    set jid [node_and_server_from_jid $user]

    if {[info exists tune(title,$connid,$jid)]} {
	append info [::msgcat::mc "\n\tTune: %s - %s" \
				  $tune(artist,$connid,$jid) \
				  $tune(title,$connid,$jid)]
    } elseif {[info exists substatus($connid,$jid)]} {
	append info [::msgcat::mc "\n\tUser's tune subscription: %s" \
				  $substatus($connid,$jid)]
    } else {
	return
    }

}

proc tune::process_tune_notification {connid jid items} {
    variable node
    variable tune

    set newartist ""
    set newlength ""
    set newsource ""
    set newtitle ""
    set newtrack ""
    set newuri ""
    set retract false
    set parsed  false

    foreach item $items {
	jlib::wrapper:splitxml $item tag vars isempty chdata children

	switch -- $tag {
	    retract {
		set retract true
	    }
	    default {
		foreach itune $children {
		    jlib::wrapper:splitxml $itune tag1 vars1 isempty1 chdata1 children1

		    if {![string equal $tag1 tune]} continue
		    set xmlns [jlib::wrapper:getattr $vars1 xmlns]
		    if {![string equal $xmlns $node]} continue

		    set parsed true

		    foreach i $children1 {
			jlib::wrapper:splitxml $i tag2 vars2 isempty2 chdata2 children2

			switch -- $tag2 {
			    artist {
				set newartist $chdata2
			    }
			    length {
				set newlength $chdata2
			    }
			    source {
				set newsource $chdata2
			    }
			    title {
				set newtitle $chdata2
			    }
			    track {
				set newtrack $chdata2
			    }
			    uri {
				set newuri $chdata2
			    }
			}
		    }
		}
	    }
	}
    }

    if {$parsed} {
	set tune(artist,$connid,$jid) $newartist
	set tune(length,$connid,$jid) $newlength
	set tune(source,$connid,$jid) $newsource
	set tune(title,$connid,$jid) $newtitle
	set tune(track,$connid,$jid) $newtrack
	set tune(uri,$connid,$jid) $newuri

	hook::run user_tune_notification_hook $connid $jid $newartist $newtitle
    } elseif {$retract} {
	catch {unset tune(artist,$connid,$jid)}
	catch {unset tune(length,$connid,$jid)}
	catch {unset tune(source,$connid,$jid)}
	catch {unset tune(title,$connid,$jid)}
	catch {unset tune(track,$connid,$jid)}
	catch {unset tune(uri,$connid,$jid)}

	hook::run user_tune_notification_hook $connid $jid "" ""
    }
}

proc tune::notify_via_status_message {connid jid artist title} {
    set contact [::roster::itemconfig $connid $jid -name]
    if {$contact == ""} {
	set contact $jid
    }

    if {$artist == ""} {
	set msg [::msgcat::mc "%s's tune is unset" $contact]
    } else {
	set msg [::msgcat::mc "%s's tune changed to %s - %s" \
			      $contact \
			      $artist \
			      $title]
    }

    set_status $msg
}

proc tune::publish {connid args} {
    variable node

    set artist ""
    set title ""
    set track ""
    set length ""
    set source ""
    set uri ""
    set callback ""
    foreach {opt val} $args {
	switch -- $opt {
	    -artist  { set artist   $val }
	    -title   { set title    $val }
	    -track   { set track    $val }
	    -length  { set length   $val }
	    -source  { set source   $val }
	    -uri     { set uri      $val }
	    -command { set callback $val }
	}
    }

    set content {}
    foreach tag {artist length source title track uri} {
	if {[set $tag] != ""} {
	    lappend content [jlib::wrapper:createtag $tag \
				 -chdata [set $tag]]
	}
    }

    set cmd [list pep::publish_item $node tune \
		  -connection $connid \
		  -payload [list [jlib::wrapper:createtag tune \
				      -vars [list xmlns $node] \
				      -subtags $content]]]

    if {$callback != ""} {
	lappend cmd -command $callback
    }

    eval $cmd
}

proc tune::unpublish {connid args} {
    variable node

    set callback ""
    foreach {opt val} $args {
	switch -- $opt {
	    -command { set callback $val }
	}
    }

    set cmd [list pep::delete_item $node tune \
		  -notify true \
		  -connection $connid]

    if {$callback != ""} {
	lappend cmd -command $callback
    }

    eval $cmd
}

proc tune::on_init {} {
    set m [pep::get_main_menu_pep_submenu]
    set mm [menu $m.tune -tearoff $::ifacetk::options(show_tearoffs)]
    $m add cascade -menu $mm \
	   -label [::msgcat::mc "User tune"]
    $mm add command -label [::msgcat::mc "Publish user tune"] \
	    -command [namespace current]::show_publish_dialog
    $mm add command -label [::msgcat::mc "Unpublish user tune"] \
	    -command [namespace current]::show_unpublish_dialog
    $mm add checkbutton -label [::msgcat::mc "Auto-subscribe to other's user tune"] \
	    -variable [namespace current]::options(auto-subscribe)
}   

proc tune::show_publish_dialog {} {
    variable tuneartist
    variable tunetitle
    variable tunetrack
    variable tunelength
    variable tunesource
    variable tuneuri
    variable myjid

    set w .user_tune
    if {[winfo exists $w]} {
	destroy $w
    }

    set connids [jlib::connections]
    if {[llength $connids] == 0} {
	tk_messageBox -icon error -title [::msgcat::mc "Error"] \
		-message [::msgcat::mc "Publishing is only possible\
					while being online"]
	return
    }

    Dialog $w -title [::msgcat::mc "User tune"] \
	    -modal none -separator 1 -anchor e -default 0 -cancel 1 -parent .
    $w add -text [::msgcat::mc "Publish"] \
	   -command [list [namespace current]::do_publish $w]
    $w add -text [::msgcat::mc "Cancel"] -command [list destroy $w]

    set f [$w getframe]

    set connjids [list [::msgcat::mc "All"]]
    foreach connid $connids {
	lappend connjids [jlib::connection_jid $connid]
    }
    set myjid [lindex $connjids 0]

    label $f.ccap -text [::msgcat::mc "Use connection:"]
    ComboBox $f.conn -editable false \
	    -values $connjids \
	    -textvariable [namespace current]::myjid
    label $f.lartist -text [::msgcat::mc "Artist:"]
    entry $f.artist -textvariable [namespace current]::tuneartist
    label $f.ltitle -text [::msgcat::mc "Title:"]
    entry $f.title -textvariable [namespace current]::tunetitle
    label $f.ltrack -text [::msgcat::mc "Track:"]
    entry $f.track -textvariable [namespace current]::tunetrack
    label $f.llength -text [::msgcat::mc "Length:"]
    entry $f.length -textvariable [namespace current]::tunelength
    label $f.lsource -text [::msgcat::mc "Source:"]
    entry $f.source -textvariable [namespace current]::tunesource
    label $f.luri -text [::msgcat::mc "URI:"]
    entry $f.uri -textvariable [namespace current]::tuneuri

    if {[llength $connjids] > 1} {
	grid $f.ccap   -row 0 -column 0 -sticky e
	grid $f.conn   -row 0 -column 1 -sticky ew
    }
    grid $f.lartist  -row 1 -column 0 -sticky e
    grid $f.artist   -row 1 -column 1 -sticky ew
    grid $f.ltitle  -row 2 -column 0 -sticky e
    grid $f.title   -row 2 -column 1 -sticky ew
    grid $f.ltrack  -row 3 -column 0 -sticky e
    grid $f.track   -row 3 -column 1 -sticky ew
    grid $f.llength  -row 4 -column 0 -sticky e
    grid $f.length   -row 4 -column 1 -sticky ew
    grid $f.lsource  -row 5 -column 0 -sticky e
    grid $f.source   -row 5 -column 1 -sticky ew
    grid $f.luri  -row 6 -column 0 -sticky e
    grid $f.uri   -row 6 -column 1 -sticky ew

    grid columnconfigure $f 1 -weight 1

    $w draw
}

proc tune::do_publish {w} {
    variable tuneartist
    variable tunetitle
    variable tunetrack
    variable tunelength
    variable tunesource
    variable tuneuri
    variable myjid

    foreach connid [jlib::connections] {
	if {[string equal $myjid [jlib::connection_jid $connid]] || \
		[string equal $myjid [::msgcat::mc "All"]]} {
	    publish $connid \
		    -artist $tuneartist \
		    -title $tunetitle \
		    -track $tunetrack \
		    -length $tunelength \
		    -source $tunesource \
		    -url $tuneuri \
		    -command [namespace current]::publish_result
	    break
	}
    }

    unset tuneartist tunetitle tunetrack tunelength tunesource tuneuri myjid
    destroy $w
}

# $res is one of: OK, ERR, DISCONNECT
proc tune::publish_result {res child} {
    switch -- $res {
	ERR {
	    set error [error_to_string $child]
	}
	default {
	    return
	}
    }

    tk_messageBox -icon error -title [::msgcat::mc "Error"] \
	-message [::msgcat::mc "User tune publishing failed: %s" $error]
}

proc tune::show_unpublish_dialog {} {
    variable myjid

    set w .user_tune
    if {[winfo exists $w]} {
	destroy $w
    }

    set connids [jlib::connections]
    if {[llength $connids] == 0} {
	tk_messageBox -icon error -title [::msgcat::mc "Error"] \
		-message [::msgcat::mc "Unpublishing is only possible\
					while being online"]
	return
    }

    Dialog $w -title [::msgcat::mc "User tune"] \
	    -modal none -separator 1 -anchor e -default 0 -cancel 1 -parent .
    $w add -text [::msgcat::mc "Unpublish"] \
	   -command [list [namespace current]::do_unpublish $w]
    $w add -text [::msgcat::mc "Cancel"] -command [list destroy $w]

    set f [$w getframe]

    set connjids [list [::msgcat::mc "All"]]
    foreach connid $connids {
	lappend connjids [jlib::connection_jid $connid]
    }
    set myjid [lindex $connjids 0]

    label $f.ccap -text [::msgcat::mc "Use connection:"]
    ComboBox $f.conn -editable false \
	    -values $connjids \
	    -textvariable [namespace current]::myjid

    if {[llength $connjids] > 1} {
	grid $f.ccap   -row 0 -column 0 -sticky e
	grid $f.conn   -row 0 -column 1 -sticky ew
    }

    grid columnconfigure $f 1 -weight 1

    if {[llength $connids] == 1} {
	do_unpublish $w
    } else {
	$w draw
    }
}

proc tune::do_unpublish {w} {
    variable myjid

    foreach connid [jlib::connections] {
	if {[string equal $myjid [jlib::connection_jid $connid]] || \
		[string equal $myjid [::msgcat::mc "All"]]} {
	    unpublish $connid \
		    -command [namespace current]::unpublish_result
	    break
	}
    }

    unset myjid
    destroy $w
}

# $res is one of: OK, ERR, DISCONNECT
proc tune::unpublish_result {res child} {
    switch -- $res {
	ERR {
	    set error [error_to_string $child]
	}
	default {
	    return
	}
    }

    tk_messageBox -icon error -title [::msgcat::mc "Error"] \
	-message [::msgcat::mc "User tune unpublishing failed: %s" $error]
}

proc tune::provide_userinfo {notebook connid jid editable} {
    variable tune
    variable m2d
    variable ::userinfo::userinfo

    if {$editable} return

    set barejid [node_and_server_from_jid $jid]
    if {![info exists tune(artist,$connid,$barejid)]} return

    foreach tag {artist length source title track uri} {
	set userinfo(tune$tag,$jid) $tune($tag,$connid,$barejid)
    }

    set f [pep::get_userinfo_dialog_pep_frame $notebook]
    set mf [userinfo::pack_frame $f.tune [::msgcat::mc "User tune"]]

    userinfo::pack_entry $jid $mf 0 tuneartist [::msgcat::mc "Artist:"]
    userinfo::pack_entry $jid $mf 1 tunetitle [::msgcat::mc "Title:"]
    userinfo::pack_entry $jid $mf 2 tunetrack [::msgcat::mc "Track:"]
    userinfo::pack_entry $jid $mf 3 tunelength [::msgcat::mc "Length:"]
    userinfo::pack_entry $jid $mf 4 tunesource [::msgcat::mc "Source:"]
    userinfo::pack_entry $jid $mf 5 tuneuri [::msgcat::mc "URI:"]
}

# vim:ts=8:sw=4:sts=4:noet
